#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import subprocess
import sys
import hashlib
from binascii import a2b_hex
from base64 import b64encode

sys.path.append('/mnt/moonbrook/Moonbrook')
sys.path.append('/mnt/moonbrook/Moonbrook/contact_team/tj_frame')
from datetime import datetime, timedelta
from copy import deepcopy

from pybloom_live import ScalableBloomFilter
from secret import batch_encrypt_decrypt_dict

reload(sys)
sys.setdefaultencoding('utf-8')


class Utils(object):
    '''工具类，用于封装'''

    @staticmethod
    def page_url_to_business_id(target_str):
        '''
        target_str -> 16位md5 -> 16进行编码 -> 12位base64编码
        @param target_str:
        @return:
        '''
        md5 = hashlib.md5()
        md5.update(target_str.encode(encoding='utf-8'))
        md5_text = md5.hexdigest()[8:24]
        hex_bytes = a2b_hex(md5_text)  # b'\xd5\xd0\xb3gc0\xd6\xa1'
        business_id = b64encode(hex_bytes).decode()
        return business_id

    @staticmethod
    def serializable_bloom(result_dic):
        '''
        递归将bloom转换成int
        @param result_dic:dict
        @return:dict
        '''
        from models import Field
        new_result_dic = {}
        for k, v in result_dic.items():
            if isinstance(k, Field):
                k = k.name
            new_result_dic[k] = deepcopy(v)

        for k, v in new_result_dic.items():
            if isinstance(v, ScalableBloomFilter):
                new_result_dic[k] = new_result_dic[k].count
            if isinstance(v, dict):
                new_result_dic[k] = Utils.serializable_bloom(v)
        return new_result_dic

    @staticmethod
    def serializable_bloom_v0(result_dic):
        '''
        递归将bloom转换成int
        @param result_dic:dict
        @return:dict
        '''
        for k, v in result_dic.items():
            if isinstance(v, ScalableBloomFilter):
                result_dic[k] = result_dic[k].count
            if isinstance(v, dict):
                Utils.serializable_bloom_v0(v)
        return result_dic

    @staticmethod
    def strf_datatime(result_dic):
        '''
        递归格式化dict中的所有datetime
        @param result_dic:dict
        @return:dict
        '''
        for k, v in result_dic.items():
            if isinstance(v, datetime):
                result_dic[k] = result_dic[k].strftime("%Y-%m-%d %H:%M:%S")
            if isinstance(v, dict):
                Utils.strf_datatime(v)
            if isinstance(v, list):
                for index, value in enumerate(v):
                    if isinstance(value, dict):
                        v[index] = Utils.strf_datatime(value)
        return result_dic

    @staticmethod
    def decrypt_one_record(record_dic, need_strf_time=False, need_decrypt=False, write_error_f=None):
        '''
        有些需求需要存json，所以要strftime，有些要原生datetime，有些要解密的数据，有些不要
        @param record_dic: dict
        @param need_strf_time: bool,是否需要格式化datetime对象
        @param need_decrypt: bool,是否需要解密整条数据
        @return: dict
        '''
        is_change_flag = False
        if need_strf_time:
            is_change_flag = True
            record_dic = Utils.strf_datatime(record_dic)
        if need_decrypt:
            is_change_flag = True
            try:
                record_dic = batch_encrypt_decrypt_dict([record_dic], 'decrypt')[0]
            except:
                if write_error_f:
                    write_error_f.write('\nbatch_encrypt_decrypt_dict {} decrypt error\n\n'.format(record_dic))
                else:
                    print('\nbatch_encrypt_decrypt_dict {} decrypt error\n\n'.format(record_dic))
        if not is_change_flag:
            # 如果既没need_strf_time又没传入need_decrypt，则抛出异常
            raise Exception('dong need decrypt_one_record')
        return record_dic

    @staticmethod
    def bloom_filter_fac(initial_capacity=1000000, error_rate=0.001, mode=2):
        '''
        bloom工厂方法配置
        @param initial_capacity: int
        @param error_rate: float
        @param mode: int  2慢但是节省内存、4快耗内存
        @return: bloom
        '''
        return ScalableBloomFilter(initial_capacity=initial_capacity, error_rate=error_rate, mode=mode)  # 慢点但是省内存。

    @staticmethod
    def get_date_range(ndays=0, end_time=None):
        '''
        得到一个时间范围
        @param ndays: int 时间间隔
        @param end_time: 结束时间 datetime or format str,默认是今天
        @return: tuple of datetime
        get_date_range(ndays=7,end_time="2021-9-30") ==>
        (datetime.datetime(2021, 9, 23, 0, 0), datetime.datetime(2021, 9, 30, 0, 0))
        '''
        n_day = timedelta(days=ndays)
        if end_time:
            if isinstance(end_time, datetime):
                start_time = end_time - n_day
            else:
                end_time = datetime.strptime(end_time, "%Y-%m-%d")
                start_time = end_time - n_day
        else:
            end_time = datetime.now()
            # today = datetime(year=2021, month=6, day=4)
            start_time = end_time - n_day
        _min = datetime(year=start_time.year, month=start_time.month, day=start_time.day)
        _max = datetime(year=end_time.year, month=end_time.month, day=end_time.day)
        return _min, _max

    @staticmethod
    def generate_abs_path(file_name, current_py_path=__file__, name='dsf'):
        '''
        生成绝对路径文件名，用于存放日志及导出、统计文件
        @param file_name: 文件名 str
        @param current_py_path: 当前文件的绝对路径 str
        @return: 绝对路径文件名  str
        '''
        current_time_str = datetime.now().strftime('%Y-%m-%d_%H')  # '2021-05-28_15'
        py_file_name = os.path.basename(current_py_path)
        abs_file_name = '/mnt5/contact_team/{}/{}/{}_{}'.format(name, py_file_name.rsplit('.', 1)[0], file_name,
                                                                current_time_str)
        return abs_file_name

    @staticmethod
    def run_shell(shell_cmd):
        '''
        运行shell命令，返回结果str
        @param shell_cmd:  "sort -u {} | wc -l".format(file_name)  str
        @return: shell命令的返回值 str
        '''
        p = subprocess.Popen(shell_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        result_str = p.stdout.read().decode('utf-8')
        return result_str


class OperationEntprise:
    '''
    doc 尽量包含operStatus\enterpriseType\socialCreditCode
    '''

    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = super(OperationEntprise, cls).__new__(cls)
        return cls._instance

    oper_status_map_file_name = 'oper_status_map.txt'
    enterprise_type_file_name = 'enterprise_type.txt'
    OPERATING = set()
    ENTERPRISE_TYPE = set()

    def __init__(self):
        self.read_from_file()

    def read_from_file(self):
        with open(self.oper_status_map_file_name, 'r') as f:
            for line in f:
                line = unicode(line.strip(), 'utf8')
                if not line:
                    continue
                self.OPERATING.add(line)
        with open(self.enterprise_type_file_name, 'r') as f:
            for line in f:
                line = unicode(line.strip(), 'utf8')
                if not line:
                    continue
                self.ENTERPRISE_TYPE.add(line)

    def check_if_operating(self, doc):
        """
        检查是否为在营和筹建的企业
        :param doc:dict
        :return:bool
        """
        if u'operStatus' not in doc:
            return False
        oper_status = doc.get(u'operStatus')
        if isinstance(oper_status, (str, unicode)):
            if isinstance(oper_status, str):
                oper_status = unicode(oper_status, 'utf8')
            if oper_status in self.OPERATING:
                return True
            return False
        if not isinstance(oper_status, dict):
            return False
        if oper_status.get(u'name', u'') in {u'营业', u'筹建'}:
            return True
        if oper_status.get(u'value', u'') in self.OPERATING:
            return True
        return False

    def check_type_qiye(self, doc):
        """
        1. 如果企业类型在企业类型列表中，则属于企业
        2. 若统一社会信用代码第二位为1，则为企业

        :param doc:dict
        :return:bool
        """
        if u'enterpriseType' in doc:
            enterprise_type = doc[u'enterpriseType']
            if isinstance(enterprise_type, str):
                enterprise_type = unicode(enterprise_type, 'utf8')
            if isinstance(enterprise_type, unicode) and enterprise_type in self.ENTERPRISE_TYPE:
                return True

        if u'socialCreditCode' in doc:
            code = doc[u'socialCreditCode']
            if isinstance(code, str):
                code = unicode(code, 'utf8')
            if isinstance(code, unicode) and len(code) in {17, 18} and code[1] == u'1':
                return True
        return False

    def check_if_operating_qiye(self, doc):
        '''
        检查是否是在营企业
        @param doc: dict
        @return: bool
        '''
        return self.check_if_operating(doc) and self.check_type_qiye(doc)
